#ifndef AER_FREECAMERA_HPP_
#define AER_FREECAMERA_HPP_

#include "aerCamera.hpp"


namespace aer {

/// + ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ +
///
/// Describes a 'Free camera' events handler
///
/// + ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ + ~ ~ +
class FreeCamera : public Camera
{    
  private:
    float m_pitchAngle;                 /// x-axis rotation angle (in radians)
    float m_yawAngle;                   /// y-axis rotation angle (in radians)
    // must be update every time the view is changed, wich is currently not always the case
    
    float m_moveCoef;
    float m_rotationCoef;
    float m_inertiaCoef;
    
    Vector3 m_moveVelocity;
    Vector2 m_rotationVelocity;
    
    bool m_bLimitPitchAngle;            /// if true, pitch is limited in the range [-pi/2, pi/2]
    bool m_bInvertPitch;                /// if true, inverts the pitch angle
    bool m_bInvertYaw;                  /// if true, inverts the yaw angle
    
    Vector2 m_cursorDelta;
    
    bool m_bEnableRotation;
    bool m_bEnableMove;

    bool m_bUseJoystick;
    

  public:
    FreeCamera(const View &view, const Frustum &frustum);

    void update();
    
    // ===========
    // + Setters +    
    // ===========
    void setMoveCoefficient(float coef)     { m_moveCoef = coef;     }
    void setRotationCoefficient(float coef) { m_rotationCoef = coef; }
    void setInertiaCoefficient(float coef)  { m_inertiaCoef = coef;  }
    
    void doLimitXAxis(bool state)  { m_bLimitPitchAngle = state; }
    void doInvertXAxis(bool state) { m_bInvertPitch = state;     }
    void doInvertYAxis(bool state) { m_bInvertYaw = state;       }
    
    void doEnableMove(bool state)     { m_bEnableMove = state;     }
    void doEnableRotation(bool state) { m_bEnableRotation = state; }
    
    void useJoystick(bool state) { m_bUseJoystick = state; }
    
    // override view setters to update Euler angles // xxx
    void setView(const  View& view)             { Camera::setView(view);           updateEulerAngles(); }    
    void setPosition(const Vector3& position)   { Camera::setPosition(position);  updateEulerAngles(); }
    void setDirection(const Vector3& direction) { Camera::setDirection(direction);updateEulerAngles(); }
    void setTarget(const Vector3& target)       { Camera::setTarget(target);      updateEulerAngles(); }
    
    
    // ===========
    // + Getters +
    // ===========
    float getMoveCoefficient()     const { return m_moveCoef;     }
    float getRotationCoefficient() const { return m_rotationCoef; }
    float getInertiaCoefficient()  const { return m_inertiaCoef;  }
    
    bool isXAxisLimited()  const { return m_bLimitPitchAngle; }
    bool isXAxisInverted() const { return m_bInvertPitch;     }
    bool isYAxisInverted() const { return m_bInvertYaw;       }


  private:
    void updateMotion();
    void updateRotation();
    void updateEulerAngles();
};
  
} // aer

#endif // AER_FREECAMERA_HPP_
